home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Routines demonstrating how to play IMA compressed WAVE files
- ** using a combination of QuickTime and the Sound Manager.
- **
- ** by Mark Cookson, Apple Developer Technical Support
- **
- ** File: IMADecompression.c
- **
- ** Copyright ©1998-1999 Apple Computer, Inc.
- ** All rights reserved.
- **
- ** You may incorporate this sample code into your applications without
- ** restriction, though the sample code has been provided "AS IS" and the
- ** responsibility for its operation is 100% yours. However, what you are
- ** not permitted to do is to redistribute the source as "Apple Sample
- ** Code" after having made changes. If you're going to re-distribute the
- ** source, we require that you make it clear in the source that the code
- ** was descended from Apple Sample Code, but that you've made changes.
- */
-
- #include <Memory.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Windows.h>
- #include <Menus.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Sound.h>
- #include <SoundInput.h>
- #include <Files.h>
- #include <Endian.h>
- #include <Navigation.h>
-
- #include <stdio.h>
- #include "SoundStruct.h"
- #include "Wave.h"
-
- typedef struct adpcmcoef_tag {
- short iCoef1;
- short iCoef2;
- } ADPCMCOEFSET;
-
- typedef struct waveformat_extended_tag {
- short wFormatTag; /* format type */
- short nChannels; /* number of channels (i.e. mono, stereo...) */
- long nSamplesPerSec; /* sample rate */
- long nAvgBytesPerSec; /* for buffer estimation */
- short nBlockAlign; /* block size of data */
- short wBitsPerSample; /* Number of bits per sample of mono data */
- short cbSize; /* The count in bytes of the extra size */
- } WAVEFORMATEX;
-
- typedef struct adpcmwaveformat_tag {
- WAVEFORMATEX wfx;
- short wSamplesPerBlock;
- short wNumCoef;
- ADPCMCOEFSET aCoef[32];
- } ADPCMWAVEFORMAT;
-
- typedef struct {
- long atomSize; // how big this structure is (big endian)
- long atomType; // atom type - always kMicrosoftADPCMFormat (big endian)
- // everything below here is little endian - right out of the wave header
- ADPCMWAVEFORMAT adpcm;
- } AtomMSADPCMWaveFormatEx;
-
- typedef struct {
- AudioFormatAtom formatData;
- AtomMSADPCMWaveFormatEx endianData;
- AudioTerminatorAtom terminatorData;
- } AudioCompressionAtom, *AudioCompressionAtomPtr, **AudioCompressionAtomHandle;
-
- // Prototypes
- OSErr InstallRequiredAppleEvents (void);
- pascal OSErr HandleOApp (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
- pascal OSErr HandleODoc (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
- pascal OSErr HandlePDoc (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
- pascal OSErr HandleQuit (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
- OSErr GetSoundToPlay (FSSpec *fileToPlay);
- OSErr PlaySound (FSSpec *fileToPlay);
-
- // Globals
- SoundInfo theSoundInfo;
- SndCommand playCmd1,
- playCmd2,
- callCmd;
- SndCommand* playCmd = nil;
- CmpSoundHeader WAVESndHeader1,
- WAVESndHeader2;
- CmpSoundHeaderPtr WAVESndHeader = nil;
- SndChannelPtr soundChan = nil;
- SoundConverter sc = nil;
- SndCallBackUPP SoundCallBackFcnUPP = nil;
- Ptr decomBuf = nil,
- decomBuf1 = nil,
- decomBuf2 = nil,
- WAVEBuffer = nil,
- compressedBuf = nil;
- unsigned long inputFrames = 0,
- framesConverted = 0,
- totalFrames = 0,
- bytesConverted = 0,
- inputBytes = 0,
- outputFrames = 0,
- outputBytes = 0,
- gSleepTime = 60;
- long length = 0;
- short whichBuffer = 0;
- Boolean gBufferDone = false,
- Quitting = false,
- gSoundPlaying = false;
-
- static pascal void SoundCallBackFcn (SndChannelPtr theChannel, SndCommand *theCmd) {
- #pragma unused (theChannel)
- #if !GENERATINGCFM
- long oldA5;
- oldA5 = SetA5 (theCmd->param2);
- #endif
-
- gBufferDone = true;
-
- #if !GENERATINGCFM
- oldA5 = SetA5 (oldA5);
- #endif
- }
-
- static OSErr MenuBarInit (void) {
- Handle menuBar;
- MenuHandle menu;
- OSErr err = noErr;
-
- menuBar = GetNewMBar (128);
- if (menuBar != nil) {
- SetMenuBar (menuBar);
- menu = GetMenuHandle (128);
- if (menu != nil) {
- AppendResMenu (menu, 'DRVR');
- DrawMenuBar ();
- } else {
- err = memFullErr;
- }
- } else {
- err = memFullErr;
- }
-
- return err;
- }
-
- static OSErr DispatchMenuChoice (long menuChoice) {
- OSErr err = noErr;
- short menu;
- short item;
- MenuHandle appleMenu;
- Str255 accName;
- short accNumber;
- FSSpec fileToPlay;
-
- if (menuChoice != 0) {
- menu = HiWord (menuChoice);
- item = LoWord (menuChoice);
- switch (menu) {
- case 128: // Apple Menu
- appleMenu = GetMenuHandle (128);
- GetMenuItemText (appleMenu, item, accName);
- accNumber = OpenDeskAcc (accName);
- break;
- case 129: // File Menu
- switch (item) {
- case 1: // Open
- err = GetSoundToPlay (&fileToPlay);
- break;
- case 3: // Quit
- Quitting = true;
- break;
- }
- }
- }
-
- HiliteMenu (0);
-
- return err;
- }
-
- static void DoIdle (void) {
- Boolean cleanup = false;
- OSErr err = noErr;
-
- if (gSoundPlaying == true && gBufferDone == true) {
- if (whichBuffer == 1) {
- playCmd = &playCmd1;
- decomBuf = decomBuf1;
- WAVESndHeader = &WAVESndHeader1;
- whichBuffer = 2;
- } else {
- playCmd = &playCmd2;
- decomBuf = decomBuf2;
- WAVESndHeader = &WAVESndHeader2;
- whichBuffer = 1;
- }
-
- if (bytesConverted < length) {
- if (bytesConverted + inputBytes > length) {
- inputBytes = length - bytesConverted;
- inputFrames = totalFrames - framesConverted;
- if (inputFrames == 0)
- inputFrames = 1;
- }
-
- BlockMoveData (WAVEBuffer + bytesConverted, compressedBuf, inputBytes);
-
- err = SoundConverterConvertBuffer (sc, compressedBuf, inputFrames, decomBuf, &outputFrames, &outputBytes);
- framesConverted += inputFrames;
- bytesConverted += inputBytes;
- WAVESndHeader->numFrames = outputFrames;
-
- gBufferDone = false;
- err = SndDoCommand (soundChan, &callCmd, true); // Reuse callBackCmd.
- err = SndDoCommand (soundChan, playCmd, true); // Play the next buffer.
- } else {
- err = SoundConverterEndConversion (sc, decomBuf, &outputFrames, &outputBytes);
- WAVESndHeader->numFrames = outputFrames;
-
- err = SndDoCommand (soundChan, playCmd, true); // Play the last buffer.
- gSoundPlaying = false;
- cleanup = true;
- }
- }
-
- if (cleanup == true) {
- if (sc != nil) {
- err = SoundConverterClose (sc);
- }
-
- if (soundChan != nil)
- err = SndDisposeChannel (soundChan, false); // wait until sounds stops playing before disposing of channel
- if (theSoundInfo.refNum)
- FSClose (theSoundInfo.refNum);
- if (SoundCallBackFcnUPP)
- DisposeRoutineDescriptor (SoundCallBackFcnUPP);
- if (decomBuf1)
- DisposePtr (decomBuf1);
- if (decomBuf2)
- DisposePtr (decomBuf2);
- if (WAVEBuffer)
- DisposePtr (WAVEBuffer);
- if (compressedBuf)
- DisposePtr (compressedBuf);
-
- soundChan = nil;
- decomBuf = nil;
- decomBuf1 = nil;
- decomBuf2 = nil;
- WAVEBuffer = nil;
- compressedBuf = nil;
- gSleepTime = 60;
- }
- }
-
- void main (void) {
- OSErr err = noErr;
- Boolean gotEvent;
- EventRecord event;
- WindowPtr window;
- short thePart;
-
- MaxApplZone ();
-
- InitGraf (&qd.thePort);
- InitFonts ();
- InitWindows ();
- InitMenus ();
- TEInit ();
- InitDialogs ((long)nil);
- InitCursor ();
-
- err = InstallRequiredAppleEvents ();
-
- err = MenuBarInit ();
-
- while (!Quitting) {
- gotEvent = WaitNextEvent (everyEvent, &event, gSleepTime, nil);
-
- if (gotEvent) {
- switch (event.what) {
- case kHighLevelEvent:
- err = AEProcessAppleEvent (&event);
- break;
- case mouseDown:
- thePart = FindWindow (event.where, &window);
- switch (thePart) {
- case inMenuBar:
- DispatchMenuChoice (MenuSelect (event.where));
- break;
- }
- break;
- case keyDown:
- if (event.modifiers & cmdKey) {
- err = DispatchMenuChoice (MenuKey (event.message & charCodeMask));
- }
- break;
- }
- } else {
- DoIdle ();
- }
- }
- }
-
- OSErr GetSoundToPlay (FSSpec *fileToPlay) {
- OSErr err = noErr;
- SFTypeList typeList = {'WAVE', 'wav ', 0, 0};
- StandardFileReply sfReply;
-
- if (NavServicesAvailable () == true) {
- NavReplyRecord navReply;
- NavDialogOptions dialogOptions;
-
- err = NavGetDefaultDialogOptions (&dialogOptions);
- if (err == noErr) {
- dialogOptions.dialogOptionFlags = kNavAllFilesInPopup;
- }
-
- if (err == noErr) {
- err = NavGetFile (nil, &navReply, &dialogOptions, nil, nil, nil, nil, nil);
- }
-
- if (navReply.validRecord && err == noErr) {
- ProcessSerialNumber processSN = {0, kCurrentProcess};
- AEAddressDesc targetAddress = {typeNull, nil};
- AppleEvent theODOC = {typeNull, nil},
- theReply = {typeNull, nil};
-
- // Create an Apple Event to ourselves.
- err = AECreateDesc (typeProcessSerialNumber, &processSN, sizeof (ProcessSerialNumber), &targetAddress);
-
- if (err == noErr) {
- // Create the open document event.
- err = AECreateAppleEvent (kCoreEventClass, kAEOpenDocuments, &targetAddress, kAutoGenerateReturnID, kAnyTransactionID, &theODOC);
- AEDisposeDesc (&targetAddress);
- }
-
- if (err == noErr) {
- // Put the list of files into the open document event Apple Event.
- err = AEPutParamDesc (&theODOC, keyDirectObject, &(navReply.selection));
- }
-
- if (err == noErr) {
- // Send the open document event to ourselves.
- err = AESend (&theODOC, &theReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
- AEDisposeDesc (&theODOC);
- AEDisposeDesc (&theReply);
- }
-
- }
-
- (void)NavDisposeReply (&navReply);
- } else {
- StandardGetFile (nil, 2, typeList, &sfReply);
-
- if (sfReply.sfGood == true) {
- *fileToPlay = sfReply.sfFile;
- err = PlaySound (fileToPlay);
- } else {
- err = userCanceledErr;
- }
- }
-
- return err;
- }
-
- OSErr PlaySound (FSSpec *fileToPlay) {
- OSErr err = noErr;
- AudioCompressionAtom decomAtom;
- SoundComponentData inputFormat,
- outputFormat;
- OSType waveFormat;
- unsigned long targetBytes = 25000;
-
- gSleepTime = 1;
-
- if (err == noErr) {
- err = FSpOpenDF (fileToPlay, fsRdPerm, &theSoundInfo.refNum);
-
- if (err == noErr) {
- // Parse the WAVE file and get the 'fmt ' atom.
- err = ASoundGetWAVEHeader (&theSoundInfo, &length, (fmtChunk*)&(decomAtom.endianData.adpcm));
- }
-
- if (err == noErr) {
- // Figure out which type of ADPCM file we have.
- switch (EndianU16_LtoN (decomAtom.endianData.adpcm.wfx.wFormatTag)) {
- case 0x0002:
- waveFormat = kMicrosoftADPCMFormat;
- break;
- case 0x0011:
- waveFormat = kDVIIntelIMAFormat;
- break;
- default:
- err = badFormat;
- }
- }
- }
-
- if (err == noErr) {
- inputFormat.flags = 0;
- inputFormat.format = waveFormat;
- inputFormat.numChannels = EndianU16_LtoN (decomAtom.endianData.adpcm.wfx.nChannels);
- inputFormat.sampleSize = EndianU16_LtoN (decomAtom.endianData.adpcm.wfx.wBitsPerSample);
- inputFormat.sampleRate = (EndianU32_LtoN (decomAtom.endianData.adpcm.wfx.nSamplesPerSec)) << 16;
- inputFormat.sampleCount = 0;
- inputFormat.buffer = nil;
- inputFormat.reserved = 0;
-
- outputFormat.flags = 0;
- outputFormat.format = kSoundNotCompressed;
- outputFormat.numChannels = EndianU16_LtoN (decomAtom.endianData.adpcm.wfx.nChannels);
- outputFormat.sampleSize = 16;
- outputFormat.sampleRate = (EndianU32_LtoN (decomAtom.endianData.adpcm.wfx.nSamplesPerSec)) << 16;
- outputFormat.sampleCount = 0;
- outputFormat.buffer = nil;
- outputFormat.reserved = 0;
-
- err = SoundConverterOpen (&inputFormat, &outputFormat, &sc);
- }
-
- if (err == noErr) {
- // Make atom to send to ADPCM decompressor so it knows how to decompress the data.
- decomAtom.formatData.size = sizeof (AudioFormatAtom);
- decomAtom.formatData.atomType = kAudioFormatAtomType;
- decomAtom.formatData.format = waveFormat;
-
- decomAtom.endianData.atomSize = sizeof (AtomMSADPCMWaveFormatEx);
- decomAtom.endianData.atomType = waveFormat;
-
- decomAtom.terminatorData.size = sizeof (AudioTerminatorAtom);
- decomAtom.terminatorData.atomType = kAudioTerminatorAtomType;
-
- err = SoundConverterSetInfo (sc, siDecompressionParams, &decomAtom);
- }
-
- if (err == noErr) {
- // Find out how many frames are in the entire sound.
- err = SoundConverterGetBufferSizes (sc, length * 4, &totalFrames, &inputBytes, &outputBytes);
- }
-
- if (err == noErr) {
- do {
- targetBytes *= 2;
- err = SoundConverterGetBufferSizes (sc, targetBytes, &inputFrames, &inputBytes, &outputBytes);
- } while (err == notEnoughBufferSpace && targetBytes < (MaxBlock () / 4));
- }
-
- if (err == noErr) {
- WAVEBuffer = NewPtr (length);
- err = MemError ();
- }
-
- if (err == noErr) {
- decomBuf1 = NewPtr (outputBytes);
- err = MemError ();
- }
-
- if (err == noErr) {
- decomBuf2 = NewPtr (outputBytes);
- err = MemError ();
- }
-
- if (err == noErr) {
- compressedBuf = NewPtr (inputBytes);
- err = MemError ();
- }
-
- if (err == noErr) {
- err = SetFPos (theSoundInfo.refNum, fsFromStart, theSoundInfo.dataStart);
- }
-
- if (err == noErr) {
- err = FSRead (theSoundInfo.refNum, &length, WAVEBuffer);
- }
-
- if (err == noErr) {
- BlockMoveData (WAVEBuffer, compressedBuf, inputBytes);
- err = SoundConverterBeginConversion (sc);
- }
-
- if (err == noErr) {
- bytesConverted = 0;
- framesConverted = 0;
- err = SoundConverterConvertBuffer (sc, compressedBuf, inputFrames, decomBuf1, &outputFrames, &outputBytes);
- bytesConverted += inputBytes;
- framesConverted += inputFrames;
- }
-
- if (err == noErr) {
- if (bytesConverted + inputBytes > length) {
- inputBytes = length - bytesConverted;
- inputFrames = totalFrames - framesConverted;
- }
- BlockMoveData (WAVEBuffer + bytesConverted, compressedBuf, inputBytes);
- err = SoundConverterConvertBuffer (sc, compressedBuf, inputFrames, decomBuf2, &outputFrames, &outputBytes);
- bytesConverted += inputBytes;
- framesConverted += inputFrames;
- }
-
- if (err == noErr) {
- SoundCallBackFcnUPP = NewSndCallBackProc (SoundCallBackFcn);
- err = SndNewChannel (&soundChan, sampledSynth, 0, SoundCallBackFcnUPP);
- }
-
- if (err == noErr) {
- WAVESndHeader1.samplePtr = decomBuf1;
- WAVESndHeader1.numChannels = outputFormat.numChannels;
- WAVESndHeader1.sampleRate = outputFormat.sampleRate;
- WAVESndHeader1.loopStart = 0;
- WAVESndHeader1.loopEnd = 0;
- WAVESndHeader1.encode = cmpSH;
- WAVESndHeader1.baseFrequency = kMiddleC;
- WAVESndHeader1.numFrames = outputFrames;
- WAVESndHeader1.AIFFSampleRate = 0; // not used
- WAVESndHeader1.markerChunk = nil;
- WAVESndHeader1.format = outputFormat.format;
- WAVESndHeader1.futureUse2 = 0;
- WAVESndHeader1.stateVars = nil;
- WAVESndHeader1.leftOverSamples = nil;
- WAVESndHeader1.compressionID = fixedCompression; // even uncompressed sounds use fixedCompression
- WAVESndHeader1.packetSize = 0; // the Sound Manager will figure this out for us
- WAVESndHeader1.snthID = 0;
- WAVESndHeader1.sampleSize = outputFormat.sampleSize;
- WAVESndHeader1.sampleArea[0] = 0; // no samples here because use samplePtr instead
-
- BlockMoveData (&WAVESndHeader1, &WAVESndHeader2, sizeof (WAVESndHeader1));
- WAVESndHeader2.samplePtr = decomBuf2;
-
- playCmd1.cmd = bufferCmd;
- playCmd1.param1 = 0; // not used, but clear it out anyway just to be safe
- playCmd1.param2 = (long)&WAVESndHeader1;
-
- playCmd2.cmd = bufferCmd;
- playCmd2.param1 = 0; // not used, but clear it out anyway just to be safe
- playCmd2.param2 = (long)&WAVESndHeader2;
-
- whichBuffer = 1; // buffer 1 will be free when callback runs
- callCmd.cmd = callBackCmd;
- callCmd.param2 = SetCurrentA5 ();
-
- gBufferDone = false;
- err = SndDoCommand (soundChan, &playCmd1, true);
- }
-
- if (err == noErr) {
- err = SndDoCommand (soundChan, &callCmd, true);
- }
-
- if (err == noErr) {
- err = SndDoCommand (soundChan, &playCmd2, true);
- gSoundPlaying = true;
- }
-
- return err;
- }
-
- OSErr InstallRequiredAppleEvents (void) {
- OSErr err;
-
- err = AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc (HandleOApp), 0, false);
-
- if (err == noErr)
- err = AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc (HandleODoc), 0, false);
-
- if (err == noErr)
- err = AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerProc (HandlePDoc), 0, false);
-
- if (err == noErr)
- err = AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc (HandleQuit), 0, false);
-
- return err;
- }
-
- pascal OSErr HandleOApp (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon) {
- #pragma unused (theAppleEvent, reply, handlerRefcon)
-
- return noErr; /* We're up and running */
- }
-
- pascal OSErr HandleODoc (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon) {
- #pragma unused (reply, handlerRefcon)
-
- AEDescList docList;
- OSErr err;
- long i = 1,
- itemsInList;
- Size actualSize;
- AEKeyword keywd;
- DescType returnedType;
-
- err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList, &docList);
- if (err == noErr) {
- err = AECountItems (&docList, &itemsInList);
- }
-
- if (err == noErr) {
- FSSpecPtr fileSpecPtr;
-
- do {
- fileSpecPtr = (FSSpecPtr)NewPtr (sizeof (FSSpec));
- err = MemError ();
-
- if (err == noErr) {
- err = AEGetNthPtr (&docList, i, typeFSS, &keywd, &returnedType, fileSpecPtr, sizeof (FSSpec), &actualSize);
- }
-
- if (err == noErr) {
- HParamBlockRec pb;
-
- pb.fileParam.ioCompletion = nil;
- pb.fileParam.ioNamePtr = fileSpecPtr->name;
- pb.fileParam.ioVRefNum = fileSpecPtr->vRefNum;
- pb.fileParam.ioDirID = fileSpecPtr->parID;
- pb.fileParam.ioFDirIndex = 0;
-
- err = PBHGetFInfoSync (&pb);
- if (err == noErr && pb.fileParam.ioFlFndrInfo.fdType != 'pref') {
- err = PlaySound (fileSpecPtr);
- DisposePtr ((Ptr)fileSpecPtr);
- }
- }
-
- i += 1;
- } while (err == noErr);
-
- // The last time through the loop we allocate a pointer we don't need.
- DisposePtr ((Ptr)fileSpecPtr);
- }
-
- (void)AEDisposeDesc (&docList);
-
- return err;
- }
-
- pascal OSErr HandlePDoc (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon) {
- #pragma unused (theAppleEvent, reply, handlerRefcon)
-
- return noErr;
- }
-
- pascal OSErr HandleQuit (AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon) {
- #pragma unused (theAppleEvent, reply, handlerRefcon)
-
- Quitting = true;
- return noErr;
- }
-